home *** CD-ROM | disk | FTP | other *** search
/ PC-X 1997 October / pcx14_9710.iso / swag / sorting.swg / 0063_Complete Collection of Sorting units.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1997-05-30  |  31.0 KB  |  1,070 lines

  1.  
  2. {
  3. > Can you show me any version of thew quick sort that you may have? I've
  4. > never seen it and never used it before. I always used an insertion sort
  5. > For anything that I was doing.
  6.  
  7. Here is one (long) non-recursive version, quite fast.
  8. }
  9.  
  10. Type
  11.   _Compare  = Function(Var A, B) : Boolean;{ QuickSort Calls This }
  12.  
  13. { --------------------------------------------------------------- }
  14. { QuickSort Algorithm by C.A.R. Hoare.  Non-Recursive adaptation  }
  15. { from "ALGORITHMS + DATA STRUCTURES = ProgramS" by Niklaus Wirth }
  16. { Prentice-Hall, 1976. Generalized For unTyped arguments.   }
  17. { --------------------------------------------------------------- }
  18.  
  19. Procedure QuickSort(V      : Pointer;   { To Array of Records }
  20.                     Cnt    : Word;      { Record Count        }
  21.                     Len    : Word;      { Record Length       }
  22.                     ALessB : _Compare); { Compare Function    }
  23.  
  24. Type
  25.   SortRec = Record
  26.     Lt, Rt : Integer
  27.   end;
  28.  
  29.   SortStak = Array [0..1] of SortRec;
  30.  
  31. Var
  32.   StkT,
  33.   StkM,
  34.   Ki, Kj,
  35.   M       : Word;
  36.   Rt, Lt,
  37.   I, J    : Integer;
  38.   Ps      : ^SortStak;
  39.   Pw, Px  : Pointer;
  40.  
  41.   Procedure Push(Left, Right : Integer);
  42.   begin
  43.     Ps^[StkT].Lt := Left;
  44.     Ps^[StkT].Rt := Right;
  45.     Inc(StkT);
  46.   end;
  47.  
  48.   Procedure Pop(Var Left, Right : Integer);
  49.   begin
  50.     Dec(StkT);
  51.     Left  := Ps^[StkT].Lt;
  52.     Right := Ps^[StkT].Rt;
  53.   end;
  54.  
  55. begin {QSort}
  56.   if (Cnt > 1) and (V <> Nil) Then
  57.   begin
  58.     StkT := Cnt - 1;    { Record Count - 1 }
  59.     Lt   := 1;          { Safety Valve    }
  60.  
  61.     { We need a stack of Log2(n-1) entries plus 1 spare For safety }
  62.  
  63.     Repeat
  64.       StkT := StkT SHR 1;
  65.       Inc(Lt);
  66.     Until StkT = 0; { 1+Log2(n-1) }
  67.  
  68.     StkM := Lt * SizeOf(SortRec) + Len + Len; { Stack Size + 2 Records }
  69.  
  70.     GetMem(Ps, StkM);   { Allocate Memory    }
  71.  
  72.     if Ps = Nil Then
  73.       RunError(215); { Catastrophic Error }
  74.  
  75.     Pw := @Ps^[Lt];   { Swap Area Pointer  }
  76.     Px := Ptr(Seg(Pw^), Ofs(Pw^) + Len); { Hold Area Pointer  }
  77.  
  78.     Lt := 0;
  79.     Rt := Cnt - 1;  { Initial Partition  }
  80.  
  81.     Push(Lt, Rt);   { Push Entire Table  }
  82.  
  83.     While StkT > 0 Do
  84.     begin  { QuickSort Main Loop }
  85.       Pop(Lt, Rt);   { Get Next Partition  }
  86.       Repeat
  87.         I := Lt; J := Rt;  { Set Work Pointers }
  88.  
  89.         { Save Record at Partition Mid-Point in Hold Area }
  90.         M := (LongInt(Lt) + Rt) div 2;
  91.         Move(Ptr(Seg(V^), Ofs(V^) + M * Len)^, Px^, Len);
  92.  
  93.         { Get Useful Offsets to speed loops }
  94.         Ki := I * Len + Ofs(V^);
  95.         Kj := J * Len + Ofs(V^);
  96.  
  97.         Repeat
  98.           { Find Left-Most Entry >= Mid-Point Entry }
  99.           While ALessB(Ptr(Seg(V^), Ki)^, Px^) Do
  100.           begin
  101.             Inc(Ki, Len);
  102.             Inc(I)
  103.           end;
  104.  
  105.           { Find Right-Most Entry <= Mid-Point Entry }
  106.           While ALessB(Px^, Ptr(Seg(V^), Kj)^) Do
  107.           begin
  108.             Dec(Kj, Len);
  109.             Dec(J)
  110.           end;
  111.  
  112.           { if I > J, the partition has been exhausted }
  113.           if I <= J Then
  114.           begin
  115.             if I < J Then  { we have two Records to exchange }
  116.             begin
  117.               Move(Ptr(Seg(V^), Ki)^, Pw^, Len);
  118.               Move(Ptr(Seg(V^), Kj)^, Ptr(Seg(V^), Ki)^, Len);
  119.               Move(Pw^, Ptr(Seg(V^), Kj)^, Len);
  120.             end;
  121.  
  122.             Inc(I);
  123.             Dec(J);
  124.             Inc(Ki, Len);
  125.             Dec(Kj, Len);
  126.           end; { if I <= J }
  127.         Until I > J;  { Until All Swaps Done }
  128.  
  129.         { We now have two partitions.  At left are all Records }
  130.         { < X, and at right are all Records > X.  The larger   }
  131.         { partition is stacked and we re-partition the residue }
  132.         { Until time to pop a deferred partition.              }
  133.  
  134.         if (J - Lt) < (Rt - I) Then { Right-Most Partition is Larger }
  135.         begin
  136.           if I < Rt Then
  137.             Push(I, Rt); { Stack Right Side }
  138.           Rt := J;    { Resume With Left }
  139.         end
  140.         else  {  Left-Most Partition is Larger }
  141.         begin
  142.           if Lt < J Then
  143.             Push(Lt, J); { Stack Left Side   }
  144.           Lt := I;    { Resume With Right }
  145.         end;
  146.  
  147.       Until Lt >= Rt;  { QuickSort is now Complete }
  148.     end;
  149.     FreeMem(Ps, StkM);   { Free Stack and Work Areas }
  150.   end;
  151. end; {QSort}
  152.  
  153. { ---------------------------   CUT  ----------------------------}
  154. {
  155. ALEXANDER CHRISTOV
  156.  
  157.  I don't know if code like this has been posted on this echo, but anyway here
  158. it goes. It implements three different versions of Qsort which so far if the
  159. fastest sorting algorithm known. However, it is not adequate For sorting File
  160. Records. I've tested the routines and have worked With them For quite a While,
  161. but don't trust me 8-) Murphy never sleeps 8-)
  162. }
  163.  
  164. Unit SORT;
  165. {─────────────────────────────────────────────────────────────────────────}
  166. { Purpose  : Unit that implements a generic QSort(), similar to           }
  167. {            the one in the standard C library.                           }
  168. { Author   : Alexander Christov                                           }
  169. { Notes    : Very instructive on the use of Pointers in TP.               }
  170. {                                                                         }
  171. {  Use freely.                                                            }
  172. {                                                                         }
  173. {─────────────────────────────────────────────────────────────────────────}
  174. Interface
  175.  
  176. Type CmpFunc=Function(El1,El2:Pointer):Boolean;
  177.  
  178. Procedure QSort(Base:Pointer;Elements,Size:Word;GT:CmpFunc);
  179.  
  180. { Base      - Pointer to the first element
  181.   Elements  - Number of elements
  182.   Size      - Size of an element in Bytes. Use SizeOf() if in doubt
  183.   GT        - A Function of Type CmpFunc that compares the elements pointed
  184.               to by the first and the second arguments and returns True
  185.               if the first is greater than the second. GT = Greater Than
  186.               8-)
  187. }
  188.  
  189. { Some commonly used CmpFunc }
  190.  
  191. Function bGT(El1,El2:Pointer):Boolean;      { Compares ^Byte }
  192. Function wGT(El1,El2:Pointer):Boolean;      { Compares ^Word }
  193. Function lGT(El1,El2:Pointer):Boolean;      { Compares ^LongInt }
  194. Function rGT(El1,El2:Pointer):Boolean;      { Compares ^Real }
  195.  
  196. Implementation
  197. {$F+}
  198.  
  199. Type Dummy=Array[0..0] of Byte;
  200.      pDummy=^Dummy;
  201.  
  202.  
  203. { Recursive Implementation }
  204.  
  205. Procedure _Sort(Base:Pointer;L,R,Size:Word;GT:CmpFunc);
  206. Var I,J:Integer;
  207. Var X:Pointer;
  208.  Procedure SwapElements(El1,El2:Word);
  209.  Var Tmp:Pointer;
  210.  begin
  211.   GetMem(Tmp,Size);
  212.   Move(pDummy(Base)^[El1*Size],Tmp^,Size);
  213.   Move(pDummy(Base)^[El2*Size],pDummy(Base)^[El1*Size],Size);
  214.   Move(Tmp^,pDummy(Base)^[El2*Size],Size);
  215.   FreeMem(Tmp,Size);
  216.  end;
  217. begin
  218.  I:=L;
  219.  J:=R;
  220.  GetMem(X,Size);
  221.  Move(pDummy(Base)^[((L+R) div 2)*Size],X^,Size);
  222.  Repeat
  223.   While GT(X,@pDummy(Base)^[I*Size]) do INC(I);
  224.   While GT(@pDummy(Base)^[J*Size],X) do DEC(J);
  225.   if I<=J then begin
  226.    if I<>J then SwapElements(I,J);
  227.    INC(I);
  228.    DEC(J);
  229.   end;
  230.  Until I>J;
  231.  FreeMem(X,Size);
  232.  if L<J then _Sort(Base,L,J,Size,GT);
  233.  if I<R then _Sort(Base,I,R,Size,GT);
  234. end;
  235.  
  236. Procedure QSort(Base:Pointer;Elements,Size:Word;GT:CmpFunc);
  237. begin
  238.  _Sort(Base,0,Elements-1,Size,GT);
  239. end;
  240.  
  241. Function bGT(El1,El2:Pointer):Boolean;
  242. Type pByte=^Byte;
  243. begin
  244.  bGt:=(pByte(El1)^>pByte(El2)^);
  245. end;
  246.  
  247. Function wGT(El1,El2:Pointer):Boolean;
  248. Type pWord=^Word;
  249. begin
  250.  wGt:=(pWord(El1)^>pWord(El2)^);
  251. end;
  252.  
  253. Function lGT(El1,El2:Pointer):Boolean;
  254. Type pLongInt=^LongInt;
  255. begin
  256.  lGt:=(pLongInt(El1)^>pLongInt(El2)^);
  257. end;
  258.  
  259. Function rGT(El1,El2:Pointer):Boolean;
  260. Type pReal=^Real;
  261. begin
  262.  rGt:=(pReal(El1)^>pReal(El2)^);
  263. end;
  264.  
  265. end.
  266.  
  267.  
  268.  
  269. {$A-,B-,D+,E-,F+,G+,I-,L+,N-,O+,P+,Q-,R-,S-,T-,V-,X+,Y+}
  270. { I don't know which settings are Really necessary For this Unit, but since
  271.   I always work With the above, I'm including them to make sure the Unit
  272.   compiles in your computer. The only critical ones (I Think) are R- and F+
  273. }
  274. Unit SORT;
  275. {─────────────────────────────────────────────────────────────────────────}
  276. { Purpose:   Unit that implements a generic QSort, similar to the         }
  277. {            one in the standard C library, but a lot more general        }
  278. {            This new version allows ordering of almost anything,         }
  279. {            even structures whose elements are not contiguous in memory  }
  280. {            or have strange mutual dependancies that don't allow "happy  }
  281. {            swapping". Obviously, this version is slower than the        }
  282. {            previous one. if you won't be sorting Linked Lists or        }
  283. {            Collections, use the previous one.                           }
  284. { Author   : Alexander Christov                                           }
  285. { Notes    : Very instructive on the use of Pointers in TP.               }
  286. {            This version does not limit the number of elements to        }
  287. {            65535 since the need not be contiguous.                      }
  288. {                                                                         }
  289. {    Use freely.                                                          }
  290. {                                                                         }
  291. {─────────────────────────────────────────────────────────────────────────}
  292. Interface
  293.  
  294. Type CmpFunc=Function(El1,El2:Pointer):Boolean;
  295.      AddrFunc=Function(Base:Pointer;Size,N:LongInt):Pointer;
  296.      SwapProc=Procedure(El1,El2:Pointer;Size:LongInt);
  297.  
  298. Procedure QSort(Base:Pointer;      { Pointer to the first element.
  299.                                      if the user Writes his own GT, Addr and
  300.                                      Swap, this isn't Really necessary.
  301.                                    }
  302.                 Elements:LongInt;  { Total number of elements }
  303.                 Size:Word;         { Size of an element in Bytes }
  304.                 GT:CmpFunc;        { Comparing Function  }
  305.                 Addr:AddrFunc;     { Addressing Function }
  306.                 Swap:SwapProc);    { Swapping Function }
  307.  
  308. {
  309.   GT        - A funcion of Type CmpFunc that compares the elements pointed
  310.               to by its first and second arguments, and returns True if the
  311.               first element is Greater Than the second one. This Unit defines
  312.               some commonly used CmpFuncs:
  313.                     bGT - Compares Bytes
  314.                     wGT - Compares Words
  315.                     lGT - Compares LongInts
  316.                     rGT - Compares Reals
  317.  
  318.   Addr      - A Function that receives the index of an element and must
  319.               return a Pointer to it.
  320.               This Unit defines the Function
  321.                    LinearAddr
  322.               which can be used whenever the elements are located
  323.               contiguously in memory.
  324.  
  325.   Swap      - A Procedure that swaps the elements pointed by its arguments.
  326.                     DirectSwap
  327.               is defined in the Unit, which can be used whenever the elements
  328.               are mutually independent or no external processes are needed
  329.               when swapping two elements
  330. }
  331.  
  332. { Commonly used CmpFuncs }
  333.  
  334. Function bGT(El1,El2:Pointer):Boolean;      { Compares ^Byte }
  335. Function wGT(El1,El2:Pointer):Boolean;      { Compares ^Word }
  336. Function lGT(El1,El2:Pointer):Boolean;      { Compares ^LongInt }
  337. Function rGT(El1,El2:Pointer):Boolean;      { Compares ^Real }
  338.  
  339. Function LinearAddr(Base:Pointer;Size,N:LongInt):Pointer;
  340. Procedure DirectSwap(El1,El2:Pointer;Size:LongInt);
  341.  
  342. Implementation
  343. {$F+}
  344.  
  345. Type Dummy=Array[0..0] of Byte;
  346.      pDummy=^Dummy;
  347.  
  348.  
  349. Var X,Middle:Pointer;
  350.  
  351. Procedure
  352. _Sort(Base:Pointer;L,R:LongInt;Size:Word;GT:CmpFunc;Addr:AddrFunc;Swap:SwapProc
  353. );
  354. Var I,J:LongInt;
  355. begin
  356.  I:=L;
  357.  J:=R;
  358.  Move(Addr(Base,Size,(L+R) div 2)^,Middle^,Size);
  359.  Repeat
  360.   While GT(Middle,Addr(Base,Size,I)) do INC(I);
  361.   While GT(Addr(Base,Size,J),Middle) do DEC(J);
  362.   if I<=J then begin
  363.    if I<>J then Swap(Addr(Base,Size,I),Addr(Base,Size,J),Size);
  364.    INC(I);
  365.    DEC(J);
  366.   end;
  367.  Until I>J;
  368.  if L<J then _Sort(Base,L,J,Size,GT,Addr,Swap);
  369.  if I<R then _Sort(Base,I,R,Size,GT,Addr,Swap);
  370. end;
  371.  
  372. Procedure QSort;
  373. begin
  374.  GetMem(X,Size);  { <- Made in Arturo Ramirez 8-) }
  375.  GetMem(Middle,Size);
  376.  _Sort(Base,0,Elements-1,Size,GT,Addr,Swap);
  377.  FreeMem(X,Size);
  378.  FreeMem(Middle,Size);
  379. end;
  380.  
  381. Function bGT(El1,El2:Pointer):Boolean;
  382. Type pByte=^Byte;
  383. begin
  384.  bGt:=(pByte(El1)^>pByte(El2)^);
  385. end;
  386.  
  387. Function wGT(El1,El2:Pointer):Boolean;
  388. Type pWord=^Word;
  389. begin
  390.  wGt:=(pWord(El1)^>pWord(El2)^);
  391. end;
  392.  
  393. Function lGT(El1,El2:Pointer):Boolean;
  394. Type pLongInt=^LongInt;
  395. begin
  396.  lGt:=(pLongInt(El1)^>pLongInt(El2)^);
  397. end;
  398.  
  399. Function rGT(El1,El2:Pointer):Boolean;
  400. Type pReal=^Real;
  401. begin
  402.  rGt:=(pReal(El1)^>pReal(El2)^);
  403. end;
  404.  
  405. { Linear Addressing }
  406.  
  407. Function LinearAddr;
  408. begin
  409.  LinearAddr:=@pdummy(Base)^[N*Size];
  410. end;
  411.  
  412. { Direct swapping of elements. With the use of Addr() it is quite more
  413.  legible 8-) }
  414.  
  415. Procedure DirectSwap;
  416. Var Tmp:Pointer;
  417. begin
  418.  GetMem(Tmp,Size);
  419.  Move(El1^,Tmp^,Size);
  420.  Move(El2^,El1^,Size);
  421.  Move(Tmp^,El2^,Size);
  422.  FreeMem(Tmp,Size);
  423. end;
  424.  
  425. end.
  426.  
  427.  
  428. { And finally a specific version of QSort() written in Assembler. It is
  429.  non recursive and sorts Arrays of Words of up to 16383 elements (since
  430.  it Uses the addresses of the elements rather than their indexes, and since
  431.  SizeOf(Word)=2 -> 16384*2=32768 "=" -32768, and the routine Uses signed
  432.  comparisons between adresses.
  433.   On my 386/33 it sorts 10 times an Array of 10000 Words in 3.6 sec, While
  434.  the first QSort() does the same in 46 sec.
  435.  
  436.   Must be called With
  437.  
  438.  Qsort(Pointer to the first element, 0, elements-1)
  439.  
  440.   Use freely. if you include the source directly in your Program, credit
  441.   must be given.
  442. }
  443.  
  444. Procedure QSort(Base:Pointer;L,R:Word);Assembler;
  445. Var TmpL,TmpR,TmpDI:Word;
  446. Asm
  447.  xor AX,AX
  448.  PUSH AX
  449.  PUSH AX     { 0 0 will act as a flag on the stack indicating that no more }
  450.  PUSH R      { (L,R) pairs need to be sorted }
  451.  PUSH L
  452. @MainLoop:
  453.  LES DI,Base
  454.  MOV TmpDI,DI
  455.  xor SI,SI
  456.  MOV BX,DI
  457.  POP AX    { AX<-L }
  458.  MOV TmpL,AX
  459.  MOV SI,AX
  460.  SHL AX,1
  461.  ADD DI,AX
  462.  POP AX    { AX<-R }
  463.  MOV TmpR,AX
  464.  and AX,AX     { R can be never 0 except if this is the (0,0) flag }
  465.  JZ @end
  466.  ADD SI,AX
  467.  SHL AX,1
  468.  ADD BX,AX
  469.  and SI,$FFFE
  470.  ADD SI,TmpDI
  471.  
  472.  { ES:DI -> Element[I] (L)
  473.    ES:BX -> Element[J] (R)
  474.    ES:SI -> Element[(L+R) div 2]
  475.  }
  476.  
  477.  MOV AX,ES:[SI]
  478. @Loop1:
  479.  MOV CX,ES:[DI]
  480.  CMP AX,CX
  481.  JNA @Loop2
  482.  ADD DI,2
  483.  JMP @Loop1
  484. @Loop2:
  485.  MOV CX,ES:[BX]
  486.  CMP CX,AX
  487.  JNA @Check
  488.  SUB BX,2
  489.  JMP @Loop2
  490. @Check:
  491.  CMP DI,BX
  492.  JG @Cont1
  493.  MOV CX,ES:[DI]
  494.  MOV DX,ES:[BX]
  495.  MOV ES:[DI],DX
  496.  MOV ES:[BX],CX
  497.  ADD DI,2
  498.  SUB BX,2
  499.  CMP DI,BX
  500.  JNG @Loop1
  501.  
  502. @Cont1:
  503.  SUB DI,TmpDI
  504.  SAR DI,1       { DI - I }
  505.  SUB BX,TmpDI
  506.  SAR BX,1       { BX - J }
  507.  CMP DI,TmpR
  508.  JGE @Cont2
  509.  PUSH TmpR      { I<R }
  510.  PUSH DI
  511. @Cont2:
  512.  CMP TmpL,BX
  513.  JGE @MainLoop
  514.  PUSH BX        { L<J }
  515.  PUSH TmpL
  516.  JMP @MainLoop
  517.  
  518. @end:
  519. end;
  520.  
  521. { ---------------------------   CUT  ----------------------------}
  522. (*
  523. From: ROLAND WODITSCH
  524. Subj: QUICK SORT
  525. *)
  526.  
  527. UNIT QSort5;
  528.  
  529. INTERFACE
  530. TYPE OrdFunction = FUNCTION(VAR a,b):BOOLEAN;
  531.  
  532. PROCEDURE Sortiere(VAR SortArray; Elementgroesse,LoIndex,HiIndex: word;
  533.                    SortKleiner: OrdFunction; von,bis:word);
  534.  
  535. {       SortArray  field to sort                                          }
  536. {       LoIndex    the lowest,                                            }
  537. {       HiIndex    the highest fieldindex like in the fielddeklarartion   }
  538. {       OrdAdr     the funktion from typ OrdFunction (s.o.)               }
  539. {       von, bis   the sortarea                                           }
  540.  
  541. {     befor calling (not befor bind!) your have to define a               }
  542. {     asymmetric  order funktion :                                        }
  543. {     function IrgendEinName(VAR x,y : TypDerFeldElemente):boolean        }
  544. {     example: (*$F+*) function kleiner(VAR x,y: integer):boolean;        }
  545. {                        begin kleiner:=x<y end;  (*$F-*)                 }
  546. {               not:  kleiner:=x<=y  (not asymmetric!)                    }
  547. {     attention: x and y must be VAR-parameters !!!                       }
  548.  
  549.  
  550.  
  551. IMPLEMENTATION
  552.  
  553. procedure Sortiere(VAR SortArray; ElementGroesse,LoIndex,HiIndex: word;
  554.                        SortKleiner:OrdFunction; von,bis:word);
  555.   type ArrayPtr = ^Byte;
  556.   var Mitte, i0, j0, m0 : ArrayPtr;
  557.  
  558.   procedure Swap(VAR x,y; size : word);
  559.     begin
  560.      INLINE ($1E/$C4/$B6/X/$C5/$BE/Y/$8B/$8E/SIZE/$E3/$0C/$26/$8A/$04/
  561.              $86/$05/$26/$88/$04/$46/$47/$E2/$F4/$1F)
  562.     end;
  563.  
  564.   function Element(i : word) : ArrayPtr;
  565.     begin
  566.       Element:=ptr(seg(SortArray),ofs(SortArray)+i*ElementGroesse)
  567.     end;
  568.  
  569.   procedure inc(var index : word; var pointer : ArrayPtr);
  570.     begin
  571.       index:=succ(index);
  572.       pointer:=ptr(seg(pointer^),ofs(pointer^)+ElementGroesse)
  573.     end;
  574.  
  575.   procedure dec(var index : word; var pointer : ArrayPtr);
  576.     begin
  577.       index:=pred(index);
  578.       pointer:=ptr(seg(pointer^),ofs(pointer^)-ElementGroesse)
  579.     end;
  580.  
  581.   procedure E_Sort(von, bis : word);
  582.     label EXIT;
  583.     var i, j : word;
  584.     begin
  585.       if bis<=von then goto EXIT;
  586.       i:=von; i0:=Element(i);
  587.       while i<bis do begin
  588.         m0:=i0; j:=i; j0:=i0; inc(j,j0);
  589.         while j<=bis do begin
  590.           if SortKleiner(j0^,m0^) then m0:=j0;
  591.           inc(j,j0)
  592.         end; (* WHILE j *)
  593.         if m0<>i0 then Swap(i0^,m0^,ElementGroesse);
  594.         inc(i,i0)
  595.       end; (* WHILE i *)
  596.       EXIT:
  597.     end; (* E_Sort *)
  598.  
  599.   procedure Sort(von, bis : word);  (* Rekursive Quicksort *)
  600.     label EXIT;
  601.     var i, j : word;
  602.     begin
  603.       if bis-von<6 then begin E_Sort(von,bis); goto EXIT end;
  604.       i:=von; j:=bis; m0:=Element((i+j) SHR 1);
  605.       move(m0^,Mitte^,ElementGroesse); i0:=Element(i); j0:=Element(j);
  606.       while i<=j do begin
  607.         while SortKleiner(i0^,Mitte^) do inc(i,i0);
  608.         while SortKleiner(Mitte^,j0^) do dec(j,j0);
  609.         if i<=j then begin
  610.           if i<>j then Swap(i0^,j0^,ElementGroesse);
  611.           inc(i,i0); dec(j,j0)
  612.         end (* if i<=j *)
  613.       end; (* while i<=j *)
  614.       if bis-i<j-von then begin
  615.                        if i<bis then Sort(i,bis);
  616.                        if von<j then Sort(von,j)
  617.                        end
  618.                      else begin
  619.                        if von<j then Sort(von,j);
  620.                        if i<bis then Sort(i,bis)
  621.                        end;
  622.       EXIT:
  623.     end; (* Sort *)
  624.  
  625.   begin
  626.     getmem(Mitte,ElementGroesse);
  627.     Sort(von-LoIndex,bis-LoIndex);
  628.     freemem(Mitte,ElementGroesse)
  629.   end; (* Sort *)
  630.  
  631. END. (* IMPLEMENTATION OF UNIT QSORT *)
  632.  
  633. { ---------------------------   CUT  ----------------------------}
  634. unit Qsort;
  635.  
  636. {TQSort by Mike Junkin 10/19/95.
  637.  DoQSort routine adapted from Peter Szymiczek's QSort procedure which
  638.  was presented in issue#8 of The Unofficial Delphi Newsletter.}
  639.  
  640. interface
  641.  
  642. uses
  643.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  644.   Forms, Dialogs;
  645.  
  646. type
  647.   TSwapEvent = procedure (Sender : TObject; e1,e2 : word) of Object;
  648.   TCompareEvent = procedure (Sender: TObject; e1,e2 : word; var Action : integer) of Object;
  649.  
  650.   TQSort = class(TComponent)
  651.   private
  652.     FCompare : TCompareEvent;
  653.     FSwap : TSwapEvent;
  654.   public
  655.     procedure DoQSort(Sender: TObject; uNElem: word);
  656.   published
  657.     property Compare : TCompareEvent read FCompare write FCompare;
  658.  
  659.     property Swap : TSwapEvent read FSwap write FSwap;
  660.   end;
  661.  
  662. procedure Register;
  663.  
  664. implementation
  665.  
  666. procedure Register;
  667. begin
  668.   RegisterComponents('Mikes', [TQSort]);
  669. end;
  670.  
  671. procedure TQSort.DoQSort(Sender: TObject; uNElem: word);
  672. { uNElem - number of elements to sort }
  673.  
  674.   procedure qSortHelp(pivotP: word; nElem: word);
  675.   label
  676.     TailRecursion,
  677.     qBreak;
  678.   var
  679.     leftP, rightP, pivotEnd, pivotTemp, leftTemp: word;
  680.     lNum: word;
  681.     retval: integer;
  682.   begin
  683.     retval := 0;
  684.     TailRecursion:
  685.       if (nElem <= 2) then
  686.  
  687.         begin
  688.           if (nElem = 2) then
  689.             begin
  690.               rightP := pivotP +1;
  691.               FCompare(Sender,pivotP,rightP,retval);
  692.               if (retval > 0) then Fswap(Sender,pivotP,rightP);
  693.             end;
  694.           exit;
  695.         end;
  696.       rightP := (nElem -1) + pivotP;
  697.       leftP :=  (nElem shr 1) + pivotP;
  698.       { sort pivot, left, and right elements for "median of 3" }
  699.       FCompare(Sender,leftP,rightP,retval);
  700.       if (retval > 0) then Fswap(Sender,leftP, rightP);
  701.       FCompare(Sender,leftP,pivotP,retval);
  702.  
  703.       if (retval > 0) then Fswap(Sender,leftP, pivotP)
  704.       else 
  705.         begin
  706.           FCompare(Sender,pivotP,rightP,retval);
  707.           if retval > 0 then Fswap(Sender,pivotP, rightP);
  708.         end;
  709.       if (nElem = 3) then
  710.         begin
  711.           Fswap(Sender,pivotP, leftP);
  712.           exit;
  713.         end;
  714.       { now for the classic Horae algorithm }
  715.       pivotEnd := pivotP + 1;
  716.       leftP := pivotEnd;
  717.       repeat
  718.         FCompare(Sender,leftP, pivotP,retval);
  719.         while (retval <= 0) do
  720.           begin
  721.  
  722.             if (retval = 0) then
  723.               begin
  724.                 Fswap(Sender,leftP, pivotEnd);
  725.                 Inc(pivotEnd);
  726.               end;
  727.             if (leftP < rightP) then
  728.               Inc(leftP)
  729.             else
  730.               goto qBreak;
  731.             FCompare(Sender,leftP, pivotP,retval);
  732.           end; {while}
  733.         while (leftP < rightP) do
  734.           begin
  735.             FCompare(Sender,pivotP, rightP,retval);
  736.             if (retval < 0) then
  737.               Dec(rightP)
  738.  
  739.             else
  740.               begin
  741.                 FSwap(Sender,leftP, rightP);
  742.                 if (retval <> 0) then
  743.                   begin
  744.                     Inc(leftP);
  745.                     Dec(rightP);
  746.                   end;
  747.                 break;
  748.               end;
  749.           end; {while}
  750.  
  751.       until (leftP >= rightP);
  752.     qBreak:
  753.       FCompare(Sender,leftP,pivotP,retval);
  754.       if (retval <= 0) then Inc(leftP);
  755.  
  756.       leftTemp := leftP -1;
  757.       pivotTemp := pivotP;
  758.       while ((pivotTemp < pivotEnd) and (leftTemp >= pivotEnd)) do
  759.         begin
  760.           Fswap(Sender,pivotTemp, leftTemp);
  761.           Inc(pivotTemp);
  762.           Dec(leftTemp);
  763.         end; {while}
  764.       lNum := (leftP - pivotEnd);
  765.       nElem := ((nElem + pivotP) -leftP);
  766.  
  767.       if (nElem < lNum) then
  768.         begin
  769.           qSortHelp(leftP, nElem);
  770.           nElem := lNum;
  771.         end
  772.       else
  773.         begin
  774.  
  775.           qSortHelp(pivotP, lNum);
  776.           pivotP := leftP;
  777.         end;
  778.       goto TailRecursion;
  779.     end; {qSortHelp }
  780.  
  781. begin
  782.   if Assigned(FCompare) and Assigned(FSwap) then
  783.   begin
  784.     if (uNElem < 2) then  exit; { nothing to sort }
  785.     qSortHelp(1, uNElem);
  786.   end;
  787. end; { QSort }
  788.  
  789. end. 
  790.  
  791. { demo }
  792.  
  793. unit Unit1;
  794.  
  795. interface
  796.  
  797. uses
  798.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  799.   Forms, Dialogs, Grids, Qsort, StdCtrls;
  800.  
  801. type
  802.   TForm1 = class(TForm)
  803.     QSort1: TQSort;
  804.     StringGrid1: TStringGrid;
  805.     Button1: TButton;
  806.     procedure FormCreate(Sender: TObject);
  807.     procedure QSort1Compare(Sender: TObject; e1, e2: Word; var Action: Integer);
  808.     procedure QSort1Swap(Sender: TObject; e1, e2: Word);
  809.     procedure Button1Click(Sender: TObject);
  810.   end;
  811.  
  812. var
  813.   Form1: TForm1;
  814.  
  815. implementation
  816.  
  817. {$R *.DFM}
  818.  
  819. procedure TForm1.FormCreate(Sender: TObject);
  820. begin
  821.  
  822.      with StringGrid1 do
  823.      begin
  824.           Cells[1,1] := 'the';
  825.           Cells[1,2] := 'brown';
  826.           Cells[1,3] := 'dog';
  827.           Cells[1,4] := 'bit';
  828.           Cells[1,5] := 'me';
  829.      end;
  830. end;
  831.  
  832. procedure TForm1.QSort1Compare(Sender: TObject; e1, e2: Word;
  833.   var Action: Integer);
  834. begin
  835.      with Sender as TStringGrid do
  836.     begin
  837.       if (Cells[1, e1] < Cells[1, e2]) then
  838.         Action := -1
  839.       else if (Cells[1, e1] > Cells[1, e2]) then
  840.  
  841.         Action := 1
  842.       else
  843.         Action := 0;
  844.     end; {with}
  845.  
  846. end;
  847.  
  848. procedure TForm1.QSort1Swap(Sender: TObject; e1, e2: Word);
  849. var
  850.   s: string[63];  { must be large enough to contain the longest string in the grid }
  851.   i: integer;
  852. begin
  853.   with Sender as TStringGrid do
  854.     for i := 0 to ColCount -1 do
  855.     begin
  856.       s := Cells[i, e1];
  857.       Cells[i, e1] := Cells[i, e2];
  858.       Cells[i, e2] := s;
  859.     end; {for}
  860.  
  861. end;
  862.  
  863. procedure TForm1.Button1Click(Sender: TObject);
  864. begin
  865.   QSort1.DoQSort(StringGrid1,STringGrid1.RowCount-1);
  866. end;
  867.  
  868. end.
  869.  
  870. { ---------------------------   CUT  ----------------------------}
  871. {
  872. > Could someone please post some code on using a quick
  873. > sort to sort an array of strings?
  874.  
  875.    I can do even better than that. I can give you some code on a general qsort
  876. routine that works like in C (if you're familiar with that). I. e. you can sort
  877. any type of arrays, if only you supply the correct compare function. Here
  878. goes...
  879. }
  880.  
  881. unit QSort;
  882. {*********************************************************
  883.  *                     QSORT.PAS                         *
  884.  *           C-like QuickSort implementation             *
  885.  *     Written 931118 by Björn Felten @ 2:203/208        *
  886.  *           After an idea by Pontus Rydin               *
  887.  *********************************************************}
  888. interface
  889. type CompFunc = function(Item1, Item2 : word) : integer;
  890.  
  891. procedure QuickSort(
  892.     var Data;
  893. {An array. Must be [0..Count-1] and not [1..Count] or anything else! }
  894.     Count,
  895. {Number of elements in the array}
  896.     Size    : word;
  897. {Size in bytes of a single element -- e.g. 2 for integers or words,
  898. 4 for longints, 256 for strings and so on }
  899.     Compare : CompFunc);
  900. {The function that decides which element is "greater" or "less". Must
  901. return an integer that's < 0 if the first element is less, 0 if they're
  902. equal and > 0 if the first element is greater. A simple Compare for
  903. words can look like this:
  904.  
  905.  function WordCompare(Item1, Item2: word): integer;
  906.  begin
  907.      WordCompare := MyArray[Item1] - MyArray[Item2]
  908.  end;
  909.  
  910. NB. It's not the =indices= that shall be compared, it's the elements that
  911. the supplied indices points to! Very important to remember!
  912. Also note that the array may be sorted in descending order just by
  913. means of a simple swap of Item1 and Item2 in the example.}
  914.  
  915. implementation
  916. procedure QuickSort;
  917.  
  918.   procedure Swap(Item1, Item2 : word);
  919.   var  P1, P2 : ^byte; I : word;
  920.   begin
  921.      if Item1 <> Item2 then
  922.      begin
  923.           I  := Size;
  924.           P1 := @Data; inc(P1, Item1 * Size);
  925.           P2 := @Data; inc(P2, Item2 * Size);
  926.           asm
  927.             mov  cx,I      { Size }
  928.             les  di,P1
  929.             push ds
  930.             lds  si,P2
  931.           @L:
  932.             mov  ah,es:[di]
  933.             lodsb
  934.             mov  [si-1],ah
  935.             stosb
  936.             loop @L
  937.             pop  ds
  938.           end
  939.       end
  940.   end;
  941.  
  942.   procedure Sort(Left, Right: integer);
  943.   var  i, j, x, y : integer;
  944.   begin
  945.      i := Left; j := Right; x := (Left+Right) div 2;
  946.      repeat
  947.         while compare(i, x) < 0 do inc(i);
  948.         while compare(x, j) < 0 do dec(j);
  949.         if i <= j then
  950.         begin
  951.            swap(i, j); inc(i); dec(j)
  952.         end
  953.      until i > j;
  954.      if Left < j then Sort(Left, j);
  955.      if i < Right then Sort(i, Right)
  956.   end;
  957.  
  958. begin Sort(0, Count) end;
  959.  
  960. end. { of unit }
  961.  
  962. { A simple testprogram can look like this: }
  963.  
  964. program QS_Test; {Test QuickSort á la C}
  965. uses qsort;
  966. var v: array[0..9999] of word;
  967.     i: word;
  968.  
  969. {$F+} {Must be compiled as FAR calls!}
  970. function cmpr(a, b: word): integer;
  971. begin cmpr := v[a] - v[b] end;
  972.  
  973. function cmpr2(a, b: word): integer;
  974. begin cmpr2 := v[b] - v[a] end;
  975. {$F-}
  976.  
  977. begin
  978.  randomize;
  979.  for i := 0 to 9999 do v[i] := random(20000);
  980.  quicksort(v, 10000, 2, cmpr);  {in order lo to hi}
  981.  quicksort(v, 10000, 2, cmpr2); {we now have a sorted list, sort it in
  982.                                 {reverse -- nasty for qsort!}
  983.  quicksort(v, 10000, 2, cmpr);  {and reverse again}
  984.  quicksort(v, 10000, 2, cmpr);  {sort a sorted list -- also not very popular}
  985. end.
  986.  
  987. { ---------------------------   CUT  ----------------------------}
  988.  
  989. {************************************************}
  990. {                                                }
  991. { QuickSort Demo                                 }
  992. { Copyright (c) 1985,90 by Borland International } { und: Robert Beicht ;-) }
  993. {                                                }
  994. {************************************************}
  995.  
  996. program QSort;
  997. {$R-,S-}
  998. uses Crt;
  999.  
  1000. { This program demonstrates the quicksort algorithm, which      }
  1001. { provides an extremely efficient method of sorting arrays in   }
  1002. { memory. The program generates a list of 1000 random numbers   }
  1003. { between 0 and 29999, and then sorts them using the QUICKSORT  }
  1004. { procedure. Finally, the sorted list is output on the screen.  }
  1005. { Note that stack and range checks are turned off (through the  }
  1006. { compiler directive above) to optimize execution speed.        }
  1007.  
  1008. const
  1009.   Max = 100;
  1010.  
  1011. type                                                                  { ***** }
  1012.   PData = ^TData;                                                     { ***** }
  1013.   TData = record                                                      { ***** }
  1014.     NachName: String[25];                                             { ***** }
  1015.     VorName:  String[25];                                             { ***** }
  1016.     {..}                                                              { ***** }
  1017.   end;                                                                { ***** }
  1018.   
  1019.   List = array[1..Max] of TData;
  1020.  
  1021. var
  1022.   Data: List;
  1023.   I: Integer;
  1024.  
  1025. function Less(var d1,d2:TData): Boolean;                              { ***** }
  1026. begin                                                                 { ***** }
  1027.   if d1.NachName < d2.NachName then Less := True  else                { ***** }
  1028.   if d1.NachName > d2.NachName then Less := False else                { ***** }
  1029.     if d1.VorName < d2.VorName then Less := True  else                { ***** }
  1030.     if d1.VorName > d2.VorName then Less := False else Less := False; { ***** }
  1031. end;                                                                  { ***** }
  1032.  
  1033. { QUICKSORT sorts elements in the array A with indices between  }
  1034. { LO and HI (both inclusive). Note that the QUICKSORT proce-    }
  1035. { dure provides only an "interface" to the program. The actual  }
  1036. { processing takes place in the SORT procedure, which executes  }
  1037. { itself recursively.                                           }
  1038.  
  1039. procedure QuickSort(var A: List; Lo, Hi: Integer);
  1040.  
  1041. procedure Sort(l, r: Integer);
  1042. var
  1043.   i, j, x: integer;                                                   { ***** }
  1044.   y: TData;                                                           { ***** }
  1045. begin
  1046.   i := l; j := r; x := (l+r) DIV 2;
  1047.   repeat
  1048.     while Less(a[i], a[x]) do i := i + 1;                             { ***** }
  1049.     while Less(a[x], a[j]) do j := j - 1;                             { ***** }
  1050.     if i <= j then
  1051.     begin
  1052.       y := a[i]; a[i] := a[j]; a[j] := y;
  1053.       i := i + 1; j := j - 1;
  1054.     end;
  1055.   until i > j;
  1056.   if l < j then Sort(l, j);
  1057.   if i < r then Sort(i, r);
  1058. end;
  1059.  
  1060. begin {QuickSort};
  1061.   Sort(Lo,Hi);
  1062. end;
  1063.  
  1064. begin {QSort}
  1065.  
  1066.   (*Initialisiere List*)
  1067.   Sort(List, 1, Count);
  1068.  
  1069. end.
  1070.